package com.qfx.demo.service;

import java.util.Collection;

import javax.servlet.http.HttpServletRequest;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.stereotype.Service;

import com.qfx.demo.cache.UserCache;
import com.qfx.demo.vo.MessageBean;
import com.qfx.demo.vo.SysUser;

@Service
public class LoginService {
	
	private final Logger logger = LogManager.getLogger(getClass());

	/**
	 * @功能描述：	保存用户注册信息
	 *
	 * @作者：zhangpj		@创建时间：2017年6月2日
	 * @param request
	 * @param sysUser
	 * @return
	 */
	public boolean saveUser(HttpServletRequest request,SysUser sysUser){
		MessageBean messageBean = new MessageBean();
		
		try {
			// 1.生成加密盐值和加密后的密码
			setUserInfo(sysUser);
			// 2.保存用户信息到缓存中
			UserCache.setUserCacheMap(sysUser.getUserName(), sysUser);
			logger.info("用户["+sysUser.getUserName()+"]注册成功");
			messageBean.setMessage("注册成功,请登录!");
			messageBean.setResult(true);
		} catch (Exception e) {
			logger.error("用户["+sysUser.getUserName()+"]注册失败");
			messageBean.setMessage("注册失败,请重新注册");
			messageBean.setObj(sysUser);
		}
		request.setAttribute("messageBean", messageBean);

		return messageBean.getResult();
	}
	
	/**
	 * @功能描述：	shiro登录验证,通过返回true,失败返回false
	 *
	 * @作者：zhangpj		@创建时间：2017年6月2日
	 * @param request
	 * @param sysUser
	 * @return
	 */
	public boolean login(HttpServletRequest request,SysUser sysUser){
		String message = "";
		MessageBean messageBean = new MessageBean();
		
		System.out.println("用户登录:userName["+sysUser.getUserName()+"],userPass["+sysUser.getPassWord()+"]");

		// 1.使用shiro进行登录验证
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken(sysUser.getUserName(), sysUser.getPassWord());
		try {
			subject.login(token);
		} catch (UnknownAccountException e) {
			message = "用户不存在!";
		} catch (IncorrectCredentialsException e) {
			message = "用户密码出错!";
		} catch (ExcessiveAttemptsException e) {
			message = "登录失败多次，账户锁定10分钟";
		} catch (DisabledAccountException e) {
			message = "帐号已被禁用";
		} catch (AuthenticationException e) {
			message = e.getMessage();
		}
		
		// 2.验证是否登录成功 (这里可以进行一些认证通过后的一些系统参数初始化操作)
        if(subject.isAuthenticated()){
            // 3.验证是否开启单用户登录
            boolean isSingleUseLogin = true;
            if (isSingleUseLogin) {
            	// 单用户登录,清除当前用户以前登录时保存的session会话
            	singleUseLogin(request, sysUser.getUserName());
			}
            // 登录成功标识
            request.getSession().setAttribute("isLogin", true);
            logger.info("用户[" + sysUser.getUserName() + "]登录认证通过");
            System.out.println("用户[" + sysUser.getUserName() + "]登录认证通过");
            messageBean.setResult(true);
        }else{
        	logger.info("用户[" + sysUser.getUserName() + "]进行登录验证失败,失败原因["+message+"]");
        	System.out.println("用户[" + sysUser.getUserName() + "]进行登录验证失败,失败原因["+message+"]");
            token.clear();
            messageBean.setMessage(message);
        }
        request.setAttribute("messageBean", messageBean);
        
		return messageBean.getResult();
	}
	
	/**
	 * @功能描述：	单用户登录,清除当前用户以前登录时保存的session会话
	 *
	 * @作者：zhangpj		@创建时间：2017年6月2日
	 * @param request
	 * @param loginName
	 */
	public void singleUseLogin(HttpServletRequest request,String loginName){
		// 1.获取当前用户sessionId
		String currentUserSessionId = request.getSession().getId();
		
		// 2.获取shiro的sessionManager
		DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
    	DefaultWebSessionManager sessionManager = (DefaultWebSessionManager)securityManager.getSessionManager();
    	
    	// 3.获取所有已登录用户的session列表
    	Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();
        
    	// 处理方法一:UserRalm中传入的是userName使用此方法
//    	String onlineSessionKey = "";
//    	for(Session onlineSession:sessions){
//    		// 4.获取已登录用户的session的key值
//    		onlineSessionKey = String.valueOf(onlineSession.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY));
//            // 5.清除当前用户以前登录时保存的session会话
//        	System.out.println(onlineSession.getId()+"------" + onlineSessionKey);
//            
//            if (onlineSessionKey.equals("null")) {
//            	sessionManager.getSessionDAO().delete(onlineSession);
//			} else if (loginName.equals(onlineSessionKey) && !onlineSession.getId().equals(currentUserSessionId)) {
//                sessionManager.getSessionDAO().delete(onlineSession);
//            }
//        }
    	
    	// 处理方法二:UserRalm中doGetAuthenticationInfo传入的是sysUser使用此方法
    	SysUser sysUser= null;
    	for(Session onlineSession:sessions){
    		// 4. 获取已登录用户的session的key值
            SimplePrincipalCollection simplePrincipalCollection = (SimplePrincipalCollection) onlineSession.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
    		
            if (null == simplePrincipalCollection) {
            	sessionManager.getSessionDAO().delete(onlineSession);
			} else {
				// 5. 获取new SimpleAuthenticationInfo(user, pwd, this.getName())中放进去的第一个参数
				sysUser= (SysUser) simplePrincipalCollection.getPrimaryPrincipal();
				
				// 5.清除当前用户以前登录时保存的session会话
				if (sysUser.getUserName().equals("null")) {
					sessionManager.getSessionDAO().delete(onlineSession);
				} else if (loginName.equals(sysUser.getUserName()) && !onlineSession.getId().equals(currentUserSessionId)) {
					sessionManager.getSessionDAO().delete(onlineSession);
					System.out.println("当前用户SessionId[" + currentUserSessionId + "],踢出其他的SessionId[" + onlineSession.getId() + "]");
				}
			}
    	}
	}
	
	/**
	 * @功能描述：	生成加密盐值和加密后的密码
	 *
	 * @作者：zhangpj		@创建时间：2017年5月18日
	 * @param sysUser
	 */
	private void setUserInfo(SysUser sysUser){
		String algorithmName = "md5";	//设置加密方式,需要与shiro配置文件中加密方式匹配
		String userName = sysUser.getUserNameReg();	// 注册名
		String passWord = sysUser.getPassWordReg(); // 注册密码
		// 生成加密盐值:随机数
		String salt = new SecureRandomNumberGenerator().nextBytes().toHex();
		int hashIterations = 3; //设置加密迭代次数,需要与shiro配置文件中加密迭代次数匹配
		
		// 生成密码时使用"用户名+盐值"来生成加密数据
		SimpleHash hash = new SimpleHash(algorithmName, passWord,userName + salt, hashIterations);
		String encodedPassword = hash.toHex();
		
		sysUser.setUserName(userName);
		sysUser.setSalt(salt);
		sysUser.setPassWord(encodedPassword);
	}
	
	public void sayHello(){
		System.out.println("ldkdkdkkkkkkkkkkkkkkkkkkkkk");
	}
}
